package net.lulihu.pangolin.headless.client.handler;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOption;
import lombok.extern.slf4j.Slf4j;
import net.lulihu.pangolin.headless.client.component.ClientNetworkComponent;
import net.lulihu.pangolin.headless.client.config.StartupConfig;
import net.lulihu.pangolin.headless.client.listener.ChannelConnectListener;
import net.lulihu.pangolin.headless.common.container.annotation.Autowired;
import net.lulihu.pangolin.headless.common.container.annotation.Component;
import net.lulihu.pangolin.headless.common.model.RealAddressConnect;
import net.lulihu.pangolin.headless.common.protocol.ProtocolMessage;
import net.lulihu.pangolin.headless.common.protocol.ProtocolMessageType;
import net.lulihu.pangolin.headless.common.protocol.handler.RegistrationMessageTypeHandlerComponent;
import net.lulihu.pangolin.headless.common.util.ChannelTwoWayBindKit;

/**
 * 真实服务器地址连接消息处理程序
 */
@Slf4j
@Component
public class RealServerAddressConnectMessageHandler implements RegistrationMessageTypeHandlerComponent {

    @Autowired
    private StartupConfig startupConfig;

    @Autowired
    private ClientNetworkComponent clientNetworkComponent;

    @Override
    public byte messageType() {
        return ProtocolMessageType.S_REAL_CONNECT;
    }

    @Override
    public void handle(ChannelHandlerContext context, ProtocolMessage message) {
        if (log.isDebugEnabled()) log.debug("收到服务器申请建立真实服务连接通道消息");

        final RealAddressConnect addressConnect = message.getBodyObj(RealAddressConnect.class);
        final String address = addressConnect.getAddress();
        final String[] addresses = address.split(":");

        clientNetworkComponent.realServerConnect(addresses[0], Integer.valueOf(addresses[1]), new ChannelConnectListener() {

            @Override
            public void connectSuccess(Channel realServerChannel) {
                if (log.isDebugEnabled()) log.debug("与真实服务器地址[{}]通道连接建立成功", address);

                // 设置不可读
                realServerChannel.config().setOption(ChannelOption.AUTO_READ, false);

                // 与代理服务器开启一个新的通道为该用户服务
                clientNetworkComponent.proxyServerConnect(startupConfig.getHost(), startupConfig.getPort(), new ChannelConnectListener() {

                    @Override
                    public void connectSuccess(Channel proxyServerChannel) {
                        if (log.isDebugEnabled()) log.debug("为真实服务器地址[{}]开辟专用代理通道连接成功", address);

                        // 双向绑定
                        ChannelTwoWayBindKit.twoWayBindingChannel(proxyServerChannel, realServerChannel);

                        // 向服务器发送双向通道连接建立成功消息
                        ProtocolMessage message = new ProtocolMessage();
                        message.setType(ProtocolMessageType.C_TWO_WAY_CHANNEL_CONNECT_SETUP);
                        message.setBodyStr(addressConnect.getChannelId());
                        proxyServerChannel.writeAndFlush(message);

                        // 设置为可读 等待数据写入
                        realServerChannel.config().setOption(ChannelOption.AUTO_READ, true);
                    }

                    @Override
                    public void connectFail() {
                        if (log.isDebugEnabled()) log.debug("为真实服务器地址[{}]开辟专用代理通道连接失败", address);

                        // 关闭真实服务通道
                        realServerChannel.close();

                        // 向服务器发送为真实服务地址开辟专用代理连接失败消息
                        ProtocolMessage message = new ProtocolMessage();
                        message.setType(ProtocolMessageType.C_DEDICATED_AGENT_CHANNEL_CONNECT_FAIL);
                        message.setBodyStr(addressConnect.getChannelId());
                        context.channel().writeAndFlush(message);
                    }
                });
            }

            @Override
            public void connectFail() {
                if (log.isDebugEnabled()) log.debug("与真实服务器地址[{}]通道连接建立失败", address);

                // 向服务器发送连接真实服务地址失败消息
                ProtocolMessage message = new ProtocolMessage();
                message.setType(ProtocolMessageType.C_REAL_CHANNEL_CONNECT_FAIL);
                message.setBodyStr(addressConnect.getChannelId());
                context.channel().writeAndFlush(message);
            }
        });
    }
}
